/// Coder: Jose Ricardo Mello Viana - 2008
/// Bugs? e-mail me: zezim.ricardo@gmail.com

#ifndef SKELETON_H_
#define SKELETON_H_

#include "typedefs.h"
#include "bone.h"

/// This class implements a generic skeleton 
class Skeleton {

public:
   /// Constructor Desfault
   Skeleton();
   
   /// Copy constructor
   Skeleton(const Skeleton& s);

   /// Constructor
   /// @param rp position of root
   /// @param h the hierarchy of skeleton
   Skeleton(Point3D rp, Hierarchy h);

   /// Destructor Default
   virtual ~Skeleton();
   
   /// Gets root position of Skeleton
   /// @return root position of Skeleton
   inline Point3D getRootPosition(){ return rootPosition; };
   
   /// Gets root transformation order of Skeleton
   /// @return root transformation order of Skeleton
   inline string getTrOrder(){ return trOrder; };
   
   /// Gets one bone of Skeleton
   /// @param n given name of bone
   /// @return a bone name n of Skeleton
   inline Bone getBone(string n){ return bones[n]; };

   /// Gets all bones of Skeleton
   /// @return all bones of Skeleton
   inline map<string, Bone> getBones(){ return bones; };
   
   /// Gets bones hierarchy of Skeleton
   /// @return bones hierarchy of Skeleton
   inline Hierarchy getHierarchy(){ return hierarchy; };
   
   /// Gets sons of one bone in hierarchy of Skeleton
   /// @param b given name of bone
   /// @return sons of bone b in hierarchy of Skeleton
   inline Sons getSons(string b){ return hierarchy[b]; };
   
   /// Gets the number of bones
   /// @return the number of bones
   inline int nBones(){ return bones.size(); };
   
   /// Sets the root position of Skeleton
   /// @param rp given new root position
   inline void setRootPosition(Point3D rp){ rootPosition = rp; };
   
   /// Sets the root transformations order of Skeleton
   /// @param tr given new root transformations order
   inline void setTrOrder(string tr){ trOrder = tr; };
 
   /// Inserts son in one bone of Skeleton hierarchy
   /// @param bone given bone for insert son
   /// @param son given son
   void insertSon(string bone, string son);
   
   /// Sets bones of skeleton
   /// @param mb given bones
   void setBones(const map<string, Bone> &mb){ bones = mb; }
   
   /// Inserts a bone in Skeleton
   /// @param *b given bone
   void insertBone(Bone* b);
   
   /// Rotates bone and your sons of Skeleton
   /// @param bone given bone name
   /// @param angles given angles over x, y and z axis
   /// @param order given order of rotations
   void rotateBone(string bone, Vector3D angles, string order, bool isRoot,
         bool isDeg);
   
   /// Returns a segment defined by the joints of bone i
   /// @return a segment defined by the joints of bone i
   Segment3D boneSegment(int i);
   
   /// Computes the point in space of the bone's source
   /// @return the source point
   Point3D point(string bone);

protected:
   Point3D                          rootPosition; /// Position of root
   string                           trOrder; /// Order of transformations for root
   map<string, Bone>                bones; /// The bones that make up skeleton
   Hierarchy                        hierarchy; /// Skeleton's hierarchy
   map<string, string>              father;
};

/// Output operator
inline ostream & operator <<(std::ostream &os, Skeleton *s) {
   os << "-------------" << endl;
   os << s->getRootPosition() << endl;
   os << s->getTrOrder() << endl;
   os << "-------" << endl;
   Hierarchy h = s->getHierarchy();
   for (Hierarchy::iterator i = h.begin(); i != h.end(); i++) {
      os << i->first << " [";
      vector<string> v = i->second;
      for(vector<string>::iterator it = v.begin(); it != v.end(); it++){
         os << *it << ", ";
      }
      os << "] " << endl;
   }
   os << "-------" << endl;
   map<string, Bone> b = s->getBones();
   for (map<string, Bone>::iterator i = b.begin(); i != b.end(); i++) {
      os << i->first << ": " << &(i->second) << endl;
   }
   os << "-------------";
   return os;
}

#endif /*SKELETON_H_*/
